<?php
/**
 * @file 3gplex/includes/node.inc
 * 
 * Node core module configuration API.
 */

require_once('./'. drupal_get_path('module', 'drupalplex') .'/includes/module.inc');

/**
 * Node type OO mapping class.
 * 
 * This class has to be compatible with the node type object drupal uses internally.
 * Drupal creates node type objects using mysql_fetch_object so we have no class to
 * inherit from.
 * 
 * The big part of the API is in plex_node_type class.
 * Drupal doesn't internally use OO except in some particular places as...
 * the node type save functions (so why I do use OO here and not there).
 * 
 * Outside of the node module this API does provide hooks for the pathauto
 * (automatically generate visitor oriented aliases to node types) and the
 * content (permits to add custom fields to node types) modules.
 * 
 * @todo Is that true? Or I just think it?
 */
class plex_node_type
{
	static $default_dir;
	/**
	 * Was this node type validated?
	 * This member is checked before operations with side effects.
	 */
	protected $validated = false;
	/**
	 * Machine-readable name of this content type.
	 * This text will be displayed as part of the list on the create content page.
	 * This name must be unique to this content type.
	 */
	protected $type = null;
	/**
	 * Human readable name of this content type.
	 * This text will be displayed as part of the list on the create content page.
	 * It is ``recommended'' that this name begins with a capital letter and consists
	 * only of letters, numbers, and spaces.
	 */
	protected $name = null;
	/**
	 * A brief description.
	 * This text will be displayed as part of the list on the create content page.
	 * HTML is interpreted as of Drupal 5.10 but is not recommended.
	 */
	protected $description = "";
	/**
	 * Explanation or submission guidelines.
	 * This text will be displayed at the top of the submission form for this content type.
	 * It is useful for helping or instructing your users.
	 */
	protected $help = "";
	/**
	 * Which module does care about it.
	 * Defines which module will have his hooks called on various operations on nodes of
	 * this type.
	 */
	protected $module = "node";
	/**
	 * 
	 */
	protected $locked = 0;
	/**
	 * 
	 */
	protected $modified = 0;
	/**
	 * 
	 */
	protected $custom = 1;
	/**
	 * 
	 */
	protected $orig_type = "";
	/**
	 * Does it have a title field?
	 */
	protected $has_title = 0;
	/**
	 * Title field label.
	 * What to prompt for as title (Name, Title, ID, Destination...)?
	 */
	protected $title_label = null;
	/**
	 * Does it have a body field?
	 */
	protected $has_body = 0;
	/**
	 * Body field label.
	 * What to prompt for as body (Story, Description, Biography, Summary...)?
	 */
	protected $body_label = null;
	/**
	 * How pathauto should alias it.
	 */
	protected $pathauto_pattern = null;
	/**
	 * List of field instances.
	 * Each field instance is read from XML as an array of properties and stored
	 * here waiting for a call to save().
	 */
	protected $content_field_instance = array();
	/**
	 * Flash video settings to this node type.
	 */
	protected $flashvideo = null;

	function __construct($typename)
	{
		$this->type = $typename;
	}

	/**
	 * Check syntax of given SimpleXMLElement xml tree and
	 * constructs a node type ready to be saved to database.
	 */
	static function build_settings_xml(SimpleXMLElement $node_type)
	{
		$settings = array();
		//
		// NODE CORE MODULE
		//
		$settings['name'] = isset($node_type->name) ? (string)$node_type->name : $this->type;
		if (isset($node_type->description)) $settings['description'] = (string)$node_type->description;
		if (isset($node_type->help)) $settings['help'] = (string)$node_type->help;
		if (isset($node_type->module)) $settings['module'] = (string)$node_type->module;
		if (isset($node_type->locked)) $settings['locked'] = (int)(boolean)$node_type->locked;
		if (isset($node_type->modified)) $settings['modified'] = (int)(boolean)$node_type->modified;
		if (isset($node_type->custom)) $settings['custom'] = (int)(boolean)$node_type->custom;
		if (isset($node_type->orig_type)) $settings['orig_type'] = (string)$node_type->orig_type;
		if (isset($node_type->title_label)) $settings['title_label'] = (string)$node_type->title_label;
		if (isset($node_type->body_label)) $settings['body_label'] = (string)$node_type->body_label;

		$modules = plex_modules::get_instance();
		//
		// PATHAUTO CONTRIB MODULE
		//
		if (isset($node_type->pattern)) {
			$settings['pathauto_pattern'] = (string)$node_type->pattern;
		}
		//
		// CONTENT CONTRIB MODULE (AND SUBMODULES)
		//
		if (isset($node_type->field_instances) && isset($node_type->field_instances->field_instance)) {
			foreach ($node_type->field_instances->field_instance as $instance) {
				$this->content_field_instance[] =
					$modules->content->build_field_instance_settings_xml($instance);
			}
		}
		// 
		// FLASHVIDEO CONTRIB MODULE
		// 
		if (isset($node_type->flashvideo)) {
			$modules->load( 'flashvideo' );
			$settings['flashvideo'] = $modules->flashvideo->build_nodetype_settings_xml($node_type->flashvideo);
		}
		return $settings;
	}

	function load_settings_xml(SimpleXMLElement $xml)
	{
		$settings = $this->build_settings_xml($xml);
		$this->load($settings);
	}

	function validate_settings(&$settings)
	{
		if ($this->type === null) throw new plex_debug_exception;
		if ($this->name === null) throw new plex_debug_exception;
		if ($this->has_body === 1 && $this->body_label === null) throw new plex_debug_exception;
		if ($this->has_title === 1 && $this->title_label === null) throw new plex_debug_exception;
		if ($this->module !== "node" && !module_exists($this->module)) throw new plex_debug_exception;
		$modules = plex_modules::get_instance();
		foreach ($this->content_field_instance as $instance) {
			$modules->load( 'content' );
			$modules->content->validate_field_instance_settings($instance);
		}
		if ($this->pathauto_pattern !== null) {
			;
		}
		if ($this->flashvideo !== null) {
			$modules->flashvideo->validate_nodetype_settings($this->type, $settings['flashvideo']);
		}
		if ($this->exists()) {
			throw new plex_debug_exception;
		}
	}

	function exists()
	{
		$query = "select 1 from {node_type} where type='%s'";
		$res = db_query($query, $this->type);
		if ($res === false || db_num_rows($res) === 0) {
			return false;
		}
		return true;
	}

	function load(array $node_type)
	{
		//
		// NODE CORE MODULE
		//
		$this->name = isset($node_type['name']) ? (string)$node_type['name'] : $this->type;
		$this->description = isset($node_type['description']) ? (string)$node_type['description'] : "";
		$this->help = isset($node_type['help']) ? (string)$node_type['help'] : "";
		$this->module = isset($node_type['module']) ? (string)$node_type['module'] : "node";
		$this->locked = isset($node_type['locked']) ? (int)$node_type['locked'] : 0;
		$this->modified = isset($node_type['modified']) ? (int)$node_type['modified'] : 0;
		$this->custom = isset($node_type['custom']) ? (int)$node_type['custom'] : 1;
		$this->orig_type = isset($node_type['orig_type']) ? (string)$node_type['orig_type'] : "";

		$this->title_label = "";
		if (isset($node_type['title_label'])) { $this->has_title = 1; $this->title_label = (string)$node_type['title_label']; }
		$this->body_label = "";
		if (isset($node_type['body_label'])) { $this->has_body = 1; $this->body_label = (string)$node_type['body_label']; }
		//
		// PATHAUTO CONTRIB MODULE
		//
		if (isset($node_type['pattern'])) {
			$this->pathauto_pattern = (string)$node_type->pattern;
		}
		//
		// CONTENT CONTRIB MODULE (AND SUBMODULES)
		//
		if (isset($node_type['field_instances'])) {
			foreach ((array)$node_type['field_instances'] as $instance) {
				$this->content_field_instance[] = $instance;
			}
		}
		// 
		// FLASHVIDEO CONTRIB MODULE
		// 
		if (isset($node_type['flashvideo'])) {
			$this->flashvideo = $node_type['flashvideo'];
		}
		return true;
	}

	/**
	 * Save this node type to database.
	 */
	function save()
	{
		$status = node_type_save($this);
		if ($status != SAVED_NEW) {
			plex::warn("Node type %node_type was not created (existing?).", array('%node_type'=>$this->name));
			return false;
		}
		$modules = plex_modules::get_instance();
		if ($this->pathauto_pattern != null) {
			$modules->load('pathauto');
			$modules->pathauto->set_node_type_pattern($this->type, $this->pathauto_pattern);
		}
		if (count($this->content_field_instance) > 0) {
			$modules->load('content');
			foreach ($this->content_field_instance as $array) {
				if (!$modules->content->require_field($array['field_name'])) {
					plex::warn("BLASTE");
					continue;
				}
				$modules->content->add_field_instance($this->type, $array['field_name'], $array['widget_type'], $array['properties']);
			}
		}
		if (!is_null($this->flashvideo)) {
			$modules->load('flashvideo');
			$modules->flashvideo->configure_nodetype($this->type, $this->flashvideo);
		}
	}
	
	/**
	 * Deletes a node type from database.
	 */
	static function rem($stringid)
	{
		node_type_delete($stringid);
		plex::notice("Deleted node type %type", array('%type'=>$stringid));
	}

	/**
	 * Creates a new node type.
	 */
	static function add($stringid, $node_type)
	{
		if (!isset($node_type->name)) $node_type->name = $stringid;
		$nt = new plex_node_type($stringid, $node_type);
		
		$status = $nt->save();
		if ($status === false) {
			return false;
		}
		plex::notice('Created new node type %name.', array('%name' => $nt->name));
		
		return $nt;
	}
}

plex_node_type::$default_dir = drupal_get_path('module', 'drupalplex').'/node_types/';

/**
 * Node OO mapping class.
 */
class plex_node_item
{
	
}

/**
 * Node core module wrapper.
 */
class plex_node extends plex_module
{
	
	function get_description()
	{
		return "Configures and loads API for Drupal core node module: Typing of your content";
	}

	function build_settings_xml($xml)
	{
		$settings = array( 'types' => array() );
		if (isset($xml->types->type)) {
			foreach ($xml->types->type as $typename) {
				$typexml = simplexml_load_file(plex_node_type::$default_dir."{$typename}.xml");
				if ($typexml === false) {
					throw new plex_debug_exception;
				}
				$settings['types'][(string)$typename] =
				$this->build_type_settings_xml((string)$typename, $typexml);
			}
		}
	}

	protected function build_type_settings_xml($typename, $typexml)
	{
		$type = new plex_node_type($typename);
		$type->load_settings_xml($typexml);
		return $type;
	}

	function validate_settings(&$settings)
	{
		if (!isset($settings['types'])) {
			$settings['types'] = array();
		}
		if (!is_array($settings['types'])) {
			$settings['types'] = array( (string)$settings['types'] );
		}
		foreach ($settings['types'] as $type) {
			$type->validate_settings($settings, $typename);
		}
	}

	protected function validate_type_settings($settings, $typename)
	{
		
	}

	function apply_settings(&$settings)
	{
		foreach ($settings['types'] as $typename) {
			$this->apply_type_settings($settings, $typename);
		}
	}

	protected function apply_type_settings(&$settings, $typename)
	{
		
	}

	/**
	 * Attempts to create a node type given a name and a SimpleXMLElement (or any object with
	 * right set of properties) which describes it.
	 */
	static function add_type($stringid, $node_type)
	{
		return plex_node_type::add($stringid, $node_type);
	}
	
	/**
	 * Given an xml description file of a node type attempts to create the described node type.
	 */
	static function add_type_file($stringid, $file)
	{
		$node_type = simplexml_load_file($path);
		if ($node_type === false) {
			plex::warn("Failed to read contact_category settings file '%file'", array('%file'=>$path));
			return false;
		}
		return add_type($stringid, $node_type);
	}
	
	/**
	 * Browse the given directory for node types xml description files and attempts to create them.
	 */
	static function add_type_dir($dir = null)
	{
		if ($dir === null) $dir = drupal_get_path('module', 'ucinstall').'/node_types/';
		$node_types = file_scan_directory($dir, '\.xml$', array('.', '..', 'CVS'), 0, true, 'filename');
		
		foreach ($node_types as $path => $file) {
			add_node_type_file(basename($path, '.xml'), $path);
		}
	}

	/**
	 * Given a SimpleXMLElement describing a vocabulary or any object with corresponding properties attempts
	 * to delete corresponding vocabulary.
	 */
	static function rem_type($type)
	{
		return plex_node_type::rem($type);
	}

	/**
	 * Given an xml description file attempts to delete corresponding node type.
	 */
	static function rem_type_file($file)
	{
		return rem_type(basename($path, '.xml'));
	}
	
	/**
	 * Browse the given directory for node types xml description files and attempts to delete them.
	 */
	static function rem_type_dir($dir = null)
	{
		if ($dir === null) $dir = drupal_get_path('module', 'drupalplex').'/node_types/';
		$node_types = file_scan_directory($dir, '\.xml$', array('.', '..', 'CVS'), 0, true, 'filename');
		
		foreach ($node_types as $path => $file) {
			plex_node_type::rem(basename($path, '.xml'));
		}
	}
}

/**
 * @page plex_node_page Node core module configuration formats
 * 
 * http://drupal.org/getting-started/before/terminology#node
 * 
 * From a more technical point of view we could say that nodes are leafs in the web
 * site data tree which the menu system adresses.
 * 
 * Each node is of a particular content type (the word content in content type is
 * not related to the @c content module) or node type.
 * 
 * Each node type host default fields like title, body, creation date or author.
 * But can also host custom fields defined using the @c content module - if enabled.
 * 
 * @section plex_node_data_types Node Data Types
 * 
 * @section plex_node_types Node Types
 * 
 * @section plex_node_examples Full Configuration Samples
 * 
 * @subsection plex_node_type_example Node Type Description
 * 
 * @c videoclip is a motion picture excerpt. It features @c content fields and
 * @c flashvideo.
 * 
 * @li uploading a movie will automatically convert it to flash
 * @li you can rate videoclip on their picture quality and how much fun you got watching it
 * @li you can refer videoclip to it's including motion picture
 * @li it has no body or long description (better to set an explicit title)
 * 
 * @verbinclude videoclip.xml
 * 
 */
